package com.tong.embeddedvr.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.tong.embeddedvr.constant.ConstantPool;
import com.tong.embeddedvr.pojo.SSHConnectInfo;
import com.tong.embeddedvr.pojo.WebSSHData;
import com.tong.embeddedvr.service.WebSSHService;
import com.tong.embeddedvr.utils.CommonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * TODO
 *
 * @author gcl
 * @version 1.0
 * @Description
 * @date 2021/3/4 17:51
 */
@Service
public class WebSSHServiceImpl implements WebSSHService {

    private static Logger logger = LoggerFactory.getLogger(WebSSHServiceImpl.class);
    private ConcurrentHashMap<String,Object> sshMap = new ConcurrentHashMap<String,Object>();
    private static ExecutorService executorService = Executors.newCachedThreadPool();

    @Override
    public void initConnection(WebSocketSession session) {
        //将信息存入sshMap中
        SSHConnectInfo sshConnectInfo = new SSHConnectInfo();
        JSch jSch = new JSch();
        sshConnectInfo.setJsch(jSch);
        sshConnectInfo.setWebSocketSession(session);
        String uuId = (String)session.getAttributes().get(ConstantPool.WEBSSH_USER_UUID);
        sshMap.put(uuId,sshConnectInfo);
    }

    @Override
    public void recvHandler(String buffer, WebSocketSession session) {
        ObjectMapper objectMapper = new ObjectMapper();
        WebSSHData prop = null;
        try {
            //将字符流解析为对象
            prop = objectMapper.readValue(buffer, WebSSHData.class);
        } catch (JsonProcessingException jsonProcessingException) {
            logger.error("字符流解析错误:[{}]",buffer);
            logger.error("错误信息:[{}]",jsonProcessingException.getMessage());
            jsonProcessingException.printStackTrace();
            return ;
        }
        String uuId = (String) session.getAttributes().get(ConstantPool.WEBSSH_USER_UUID);
        if(ConstantPool.WEBSSH_OPERATE_CONNECT.equals(prop.getOperate())){
            SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(uuId);
            final WebSSHData finalProp = prop;
            executorService.execute(new Thread(){
                @Override
                public void run() {
                    try {
                        connectToSSH(sshConnectInfo,finalProp,session);
                    } catch (Exception e) {
                        System.out.println(finalProp);
                        logger.error("webssh连接异常");
                        logger.error("错误信息:[{}]",e.getMessage());
                        closeConnection(session);
                    }
                }
            });
        }else if(ConstantPool.WEBSSH_OPERATE_COMMAND.equals(prop.getOperate())){
            SSHConnectInfo sshConnectInfo = (SSHConnectInfo)sshMap.get(uuId);
            if(sshConnectInfo!=null){
                try {
                    transToSSH(sshConnectInfo.getChannel(),prop.getCommand());
                } catch (IOException e) {
                    logger.error("webssh连接异常");
                    logger.error("错误信息:[{}]",e.getMessage());
                    closeConnection(session);
                }
            }else{
                logger.error("当前uuid无对应的连接信息");
            }
        }else{
            logger.error("不支持的操作");
            closeConnection(session);
        }
    }

    @Override
    public void sendMsg(WebSocketSession session, byte[] buffer) throws IOException {  //信息会写到前端
        session.sendMessage(new TextMessage(buffer));
    }

    @Override
    public void closeConnection(WebSocketSession session) {
        String uuId = (String)session.getAttributes().get(ConstantPool.WEBSSH_USER_UUID);
        SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(uuId);
        if(sshConnectInfo!=null){
            Channel channel = sshConnectInfo.getChannel();
            if(channel!=null){
                channel.disconnect();
            }
        }
        sshMap.remove(uuId);
    }


    private void connectToSSH(SSHConnectInfo sshConnectInfo, WebSSHData webSSHData, WebSocketSession webSocketSession) throws JSchException, IOException {
        Session session = null;
        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        //获取jsch的会话
        session = sshConnectInfo.getJsch().getSession(webSSHData.getHostAccountUsername(), webSSHData.getHostIp(), webSSHData.getHostPort());
        session.setConfig(config);
        //设置密码
        session.setPassword(webSSHData.getHostAccountPassword());
        //连接  超时时间30s
        session.connect(30000);

        //开启shell通道
        Channel channel = session.openChannel("shell");

        //通道连接 超时时间3s
        channel.connect(3000);

        //设置channel
        sshConnectInfo.setChannel(channel);

        //转发消息
        transToSSH(channel, "\r");

        //读取终端返回的信息流
        InputStream inputStream = channel.getInputStream();  //输出流
        try {
            //循环读取
            byte[] buffer = new byte[1024];
            int i = 0;
            //如果没有数据来，线程会一直阻塞在这个地方等待数据。
            while ((i = inputStream.read(buffer)) != -1) {
                sendMsg(webSocketSession, Arrays.copyOfRange(buffer, 0, i));  //发送到前端
            }

        } finally {
            //断开连接后关闭会话
            session.disconnect();
            channel.disconnect();
            if (inputStream != null) {
                inputStream.close();
            }
        }

    }

    private void transToSSH(Channel channel, String command) throws IOException {
        if (channel != null) {
            OutputStream outputStream = channel.getOutputStream();
            outputStream.write(command.getBytes());
            outputStream.flush();
        }
    }
}
